package io.gitee.zhangbinhub.admin.oauth.controller.api

import io.gitee.zhangbinhub.acp.boot.exceptions.WebException
import io.gitee.zhangbinhub.acp.boot.interfaces.LogAdapter
import io.gitee.zhangbinhub.acp.cloud.annotation.AcpCloudDuplicateSubmission
import io.gitee.zhangbinhub.acp.core.common.CommonTools
import io.gitee.zhangbinhub.admin.common.api.OauthApi
import io.gitee.zhangbinhub.admin.common.constant.ModuleFuncCode
import io.gitee.zhangbinhub.admin.common.vo.BooleanInfoVo
import io.gitee.zhangbinhub.admin.common.vo.InfoVo
import io.gitee.zhangbinhub.admin.oauth.constant.AuthConfigExpression
import io.gitee.zhangbinhub.admin.oauth.domain.MenuDomain
import io.gitee.zhangbinhub.admin.oauth.domain.ModuleFuncDomain
import io.gitee.zhangbinhub.admin.oauth.entity.Menu
import io.gitee.zhangbinhub.admin.oauth.entity.ModuleFunc
import io.gitee.zhangbinhub.admin.oauth.po.MenuPo
import io.gitee.zhangbinhub.admin.oauth.po.ModuleFuncPo
import io.gitee.zhangbinhub.admin.oauth.vo.MenuVo
import io.gitee.zhangbinhub.admin.oauth.vo.ModuleFuncVo
import io.gitee.zhangbinhub.admin.resource.server.base.BaseResourceServerController
import io.gitee.zhangbinhub.admin.resource.server.tools.TokenTools
import io.swagger.v3.oas.annotations.*
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.annotation.PostConstruct
import jakarta.validation.Valid
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.NotEmpty
import jakarta.validation.constraints.NotNull
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.http.HttpStatus
import org.springframework.http.MediaType
import org.springframework.http.ResponseEntity
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*

/**
 * @author zhang by 16/01/2019
 * @since JDK 11
 */
@Validated
@RestController
@RequestMapping(OauthApi.basePath)
@Tag(name = "权限信息")
class AuthController @Autowired
constructor(
    private val logAdapter: LogAdapter,
    private val tokenTools: TokenTools,
    private val menuDomain: MenuDomain,
    private val moduleFuncDomain: ModuleFuncDomain
) : BaseResourceServerController(logAdapter) {

    private val moduleFuncCodeList: MutableList<String> = mutableListOf()

    @Operation(summary = "获取模块功能编码列表")
    @PreAuthorize(AuthConfigExpression.authConfig)
    @GetMapping(value = [OauthApi.moduleFuncCodes], produces = [MediaType.APPLICATION_JSON_VALUE])
    fun findModuleFuncCode(): ResponseEntity<List<String>> = ResponseEntity.ok(moduleFuncCodeList)

    @PostConstruct
    fun init() {
        try {
            for (field in ModuleFuncCode::class.java.declaredFields) {
                val value = field.get(ModuleFuncCode::class.java)
                if (value is String) {
                    moduleFuncCodeList.add(value)
                }
            }
            moduleFuncCodeList.sort()
        } catch (e: Exception) {
            logAdapter.error(e.message, e)
        }

    }

    @Operation(summary = "判断当前用户是否具有指定的权限")
    @GetMapping(value = [OauthApi.authentication + "/{authentication}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun currUserHasAuthentication(
        @Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication,
        @PathVariable authentication: String
    ): ResponseEntity<BooleanInfoVo> =
        ResponseEntity.ok(
            BooleanInfoVo(
                result = hasAuthentication(
                    bearerTokenAuthentication,
                    mutableListOf(authentication)
                )
            )
        )

    @Operation(summary = "获取当前用户所属菜单", description = "根据当前登录的用户信息，查询有权访问的菜单列表")
    @GetMapping(value = [OauthApi.currMenu], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun currMenuList(@Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication): ResponseEntity<List<Menu>> =
        ResponseEntity.ok(
            menuDomain.getMenuList(
                tokenTools.getAuthenticatedPrincipal(bearerTokenAuthentication).clientId ?: "",
                bearerTokenAuthentication.name
            )
        )

    @Operation(summary = "获取当前用户所有功能权限信息", description = "根据当前登录的用户信息，查询具备的功能权限")
    @GetMapping(value = [OauthApi.currModuleFunc], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun currModuleFuncList(@Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication): ResponseEntity<List<ModuleFunc>> =
        ResponseEntity.ok(
            moduleFuncDomain.getModuleFuncList(
                tokenTools.getAuthenticatedPrincipal(bearerTokenAuthentication).clientId ?: "",
                bearerTokenAuthentication.name
            )
        )

    @Operation(summary = "获取指定应用下的菜单列表", description = "查询指定应用的菜单列表，供选择配置")
    @PreAuthorize(AuthConfigExpression.authQuery)
    @GetMapping(value = [OauthApi.menuList + "/{appId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun menuList(@PathVariable appId: String): ResponseEntity<List<Menu>> =
        ResponseEntity.ok(menuDomain.getMenuListByAppId(appId))

    @Operation(summary = "获取指定应用下的模块功能列表", description = "查询指定应用的模块功能列表，供选择配置")
    @PreAuthorize(AuthConfigExpression.authQuery)
    @GetMapping(value = [OauthApi.moduleFuncList + "/{appId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun moduleFuncList(@PathVariable appId: String): ResponseEntity<List<ModuleFunc>> =
        ResponseEntity.ok(moduleFuncDomain.getModuleFuncListByAppId(appId))

    @Operation(summary = "获取菜单列表", description = "查询所有菜单列表")
    @PreAuthorize(AuthConfigExpression.authQuery)
    @GetMapping(value = [OauthApi.menuConfig], produces = [MediaType.APPLICATION_JSON_VALUE])
    fun allMenuList(): ResponseEntity<List<Menu>> = ResponseEntity.ok(menuDomain.getAllMenuList())

    @Operation(summary = "获取模块功能列表", description = "查询所有模块功能列表")
    @PreAuthorize(AuthConfigExpression.authQuery)
    @GetMapping(value = [OauthApi.moduleFuncConfig], produces = [MediaType.APPLICATION_JSON_VALUE])
    fun allModuleFuncList(): ResponseEntity<List<ModuleFunc>> =
        ResponseEntity.ok(moduleFuncDomain.getAllModuleFuncList())

    @Operation(summary = "新建菜单信息", description = "名称、应用ID、图标、链接、上级、序号、模式、状态、关联角色")
    @ApiResponses(ApiResponse(responseCode = "201", description = "创建成功"))
    @PreAuthorize(AuthConfigExpression.authAdd)
    @PutMapping(value = [OauthApi.menuConfig], produces = [MediaType.APPLICATION_JSON_VALUE])
    @AcpCloudDuplicateSubmission
    fun addMenu(
        @Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication,
        @RequestBody @Valid menuPo: MenuPo
    ): ResponseEntity<Menu> =
        menuDomain.doCreate(bearerTokenAuthentication, menuPo).let {
            ResponseEntity.status(HttpStatus.CREATED).body(it)
        }

    @Operation(summary = "新建模块功能信息", description = "名称、应用ID、编码、上级、关联角色")
    @ApiResponses(ApiResponse(responseCode = "201", description = "创建成功"))
    @PreAuthorize(AuthConfigExpression.authAdd)
    @PutMapping(value = [OauthApi.moduleFuncConfig], produces = [MediaType.APPLICATION_JSON_VALUE])
    @AcpCloudDuplicateSubmission
    fun addModuleFunc(
        @Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication,
        @RequestBody @Valid moduleFuncPo: ModuleFuncPo
    ): ResponseEntity<ModuleFunc> =
        moduleFuncDomain.doCreate(bearerTokenAuthentication, moduleFuncPo).let {
            ResponseEntity.status(HttpStatus.CREATED).body(it)
        }

    @Operation(summary = "删除指定的菜单信息")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；存在下级，不允许删除；"))
    @PreAuthorize(AuthConfigExpression.authDelete)
    @DeleteMapping(value = [OauthApi.menuConfig], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun deleteMenu(
        @Parameter(description = "id列表", required = true)
        @NotEmpty(message = "id不能为空")
        @NotNull(message = "id不能为空")
        @RequestBody
        idList: MutableList<String>
    ): ResponseEntity<InfoVo> =
        menuDomain.doDelete(idList).let { ResponseEntity.ok(InfoVo(message = "删除成功")) }

    @Operation(summary = "删除指定的模块功能信息")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；存在下级，不允许删除；"))
    @PreAuthorize(AuthConfigExpression.authDelete)
    @DeleteMapping(value = [OauthApi.moduleFuncConfig], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun deleteModuleFunc(
        @Parameter(description = "id列表", required = true)
        @NotEmpty(message = "id不能为空")
        @NotNull(message = "id不能为空")
        @RequestBody
        idList: MutableList<String>
    ): ResponseEntity<InfoVo> =
        moduleFuncDomain.doDelete(idList).let { ResponseEntity.ok(InfoVo(message = "删除成功")) }

    @Operation(summary = "更新菜单信息", description = "名称、应用ID、图标、链接、上级、序号、模式、状态、关联角色")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；ID不能为空；找不到信息；"))
    @PreAuthorize(AuthConfigExpression.authUpdate)
    @PatchMapping(value = [OauthApi.menuConfig], produces = [MediaType.APPLICATION_JSON_VALUE])
    @AcpCloudDuplicateSubmission
    @Throws(WebException::class)
    fun updateMenu(
        @Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication,
        @RequestBody @Valid menuPo: MenuPo
    ): ResponseEntity<Menu> {
        if (CommonTools.isNullStr(menuPo.id)) {
            throw WebException("配置ID不能为空")
        }
        return ResponseEntity.ok(menuDomain.doUpdate(bearerTokenAuthentication, menuPo))
    }

    @Operation(summary = "更新模块功能信息", description = "名称、应用ID、编码、上级、关联角色")
    @ApiResponses(
        ApiResponse(
            responseCode = "400",
            description = "参数校验不通过；模块功能编码非法，请重新输入；没有权限做此操作；ID不能为空；找不到信息；"
        )
    )
    @PreAuthorize(AuthConfigExpression.authUpdate)
    @PatchMapping(value = [OauthApi.moduleFuncConfig], produces = [MediaType.APPLICATION_JSON_VALUE])
    @AcpCloudDuplicateSubmission
    @Throws(WebException::class)
    fun updateModuleFunc(
        @Parameter(hidden = true) bearerTokenAuthentication: BearerTokenAuthentication,
        @RequestBody @Valid moduleFuncPo: ModuleFuncPo
    ): ResponseEntity<ModuleFunc> {
        if (CommonTools.isNullStr(moduleFuncPo.id)) {
            throw WebException("配置ID不能为空")
        }
        return ResponseEntity.ok(moduleFuncDomain.doUpdate(bearerTokenAuthentication, moduleFuncPo))
    }

    @Operation(summary = "获取菜单详细信息")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；ID不能为空；找不到信息；"))
    @PreAuthorize(AuthConfigExpression.authQuery)
    @GetMapping(value = [OauthApi.menuConfig + "/{menuId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun menuInfo(
        @Parameter(description = "菜单id", required = true)
        @NotBlank(message = "菜单id不能为空")
        @PathVariable
        menuId: String
    ): ResponseEntity<MenuVo> =
        ResponseEntity.ok(menuDomain.getMenuInfo(menuId))

    @Operation(summary = "获取模块功能详细信息")
    @ApiResponses(ApiResponse(responseCode = "400", description = "参数校验不通过；ID不能为空；找不到信息；"))
    @PreAuthorize(AuthConfigExpression.authQuery)
    @GetMapping(value = [OauthApi.moduleFuncConfig + "/{moduleFuncId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
    @Throws(WebException::class)
    fun moduleFuncInfo(
        @Parameter(description = "模块功能id", required = true)
        @NotBlank(message = "模块功能id不能为空")
        @PathVariable
        moduleFuncId: String
    ): ResponseEntity<ModuleFuncVo> =
        ResponseEntity.ok(moduleFuncDomain.getModuleFuncInfo(moduleFuncId))

}
